#include "lib_drm.h"

#include "mpp_buffer.h"


//****************************************************************************

#define CODEC_ALIGN(x, a)   (((x)+(a)-1)&~((a)-1))
#define rkdrm_log(fmt,...)\
            do {\
                printf(fmt,##__VA_ARGS__);\
            } while(0)


//****************************************************************************

struct sp_dev *mDev = NULL;
struct sp_plane **mPlanes;
struct sp_crtc *mCrtc;
struct sp_plane *mTestPlane;
struct sp_plane *mTestPlane_rgb888;


//****************************************************************************

int rkdrm_init(void)
{
    int i, ret = 0;

    mDev = create_sp_dev();
    if (!mDev) {
        rkdrm_log("failed to exec create_sp_dev.\n");
        return -1;
    }

    ret = initialize_screens(mDev);
    if (ret != 0) {
        rkdrm_log("failed to exec initialize_screens.\n");
        return -2;
    }

    mPlanes = (struct sp_plane **)calloc(mDev->num_planes, sizeof(*mPlanes));
    if (!mPlanes) {
        rkdrm_log("failed to calloc mPlanes.\n");
        return -3;
    }

    mCrtc = &mDev->crtcs[0];
    for (i = 0; i < mCrtc->num_planes; i++) {
        mPlanes[i] = get_sp_plane(mDev, mCrtc);
        if (is_supported_format(mPlanes[i], DRM_FORMAT_NV12))
            mTestPlane = mPlanes[i];

		if (is_supported_format(mPlanes[i], DRM_FORMAT_RGB888))
            mTestPlane_rgb888 = mPlanes[i];
    }

    if (!mTestPlane) {
        rkdrm_log("failed to get mTestPlane.\n");
        return -4;
    }


    if (!mTestPlane_rgb888) {
        rkdrm_log("failed to get mTestPlane_rgb888.\n");
        return -4;
    }	

	log_e("mDev->crtcs[1].scanout=%x\n",mDev->crtcs[1].scanout);

	if(mDev->crtcs[0].scanout)
		fill_bo(mDev->crtcs[0].scanout, 0xFF, 0, 0, 0);

	if(mDev->crtcs[1].scanout)
		fill_bo(mDev->crtcs[1].scanout, 0xFF, 0, 0, 0);

    return 0;
}


int rkdrm_show_argb(uint32_t x, uint32_t y, uint32_t width,uint32_t height, uint8_t a, uint8_t r, uint8_t g, uint8_t b)
{
	struct sp_bo* bo = NULL;

	uint32_t i, j, xmax = x + width, ymax = y + height;

	if(!mDev->crtcs[1].scanout)
		return -1;

	bo = mDev->crtcs[1].scanout;

#if 0
	log_i("width  =%d\n",bo->width);
	log_i("height =%d\n",bo->height);
	log_i("pitch  =%d\n",bo->pitch);
#endif

    if (xmax > bo->width)
        xmax = bo->width;
    if (ymax > bo->height)
        ymax = bo->height;

	memset(bo->map_addr,0,bo->size);

    for (i = y; i < ymax; i++) {
        uint8_t* row = (uint8_t*)bo->map_addr + i * bo->pitch;

        for (j = x; j < xmax; j++) {
            uint8_t* pixel = row + j * 4;

            if (bo->format == DRM_FORMAT_ARGB8888 || bo->format == DRM_FORMAT_XRGB8888) {
                pixel[0] = b;
                pixel[1] = g;
                pixel[2] = r;
                pixel[3] = a;
            } else if (bo->format == DRM_FORMAT_RGBA8888) {
                pixel[0] = r;
                pixel[1] = g;
                pixel[2] = b;
                pixel[3] = a;
            }
        }
    }

	return 0;
}


int rkdrm_show_buff_edp(uint32_t x, uint32_t y, uint32_t width,uint32_t height, const char *buff)
{
   struct sp_bo* bo = NULL;

   uint8_t a = 0;
   uint8_t r = 0;
   uint8_t g = 0;
   uint8_t b = 0;

   uint8_t* pixel = NULL;

   uint32_t i, j, xmax = x + width, ymax = y + height;


   if(!mDev->crtcs[1].scanout)
	   return -1;

   bo = mDev->crtcs[1].scanout;


#if 1
   if (xmax > bo->width)
	   xmax = bo->width;
   if (ymax > bo->height)
	   ymax = bo->height;
#endif

	for(i=0;i<EDP_LCD_HEIGHT;i++)
	{
		uint8_t* row = (uint8_t*)bo->map_addr + i * bo->pitch;

		for(j=0;j<EDP_LCD_WIDTH;j++)
		{
			if((i >= y)&&(i < (y + height))&&(j >= x)&&(j < (x + width)))
			{
				pixel = row + j * 4;
				
				r = (*(buff + 0))&0x000000ff;
				g = (*(buff + 1))&0x000000ff;
				b = (*(buff + 2))&0x000000ff; 
						
				buff = buff + 3;

				
				if (bo->format == DRM_FORMAT_ARGB8888 || bo->format == DRM_FORMAT_XRGB8888) {

					//log_i("bo->format argb888\n");

					pixel[0] = b;
					pixel[1] = g;
					pixel[2] = r;
					pixel[3] = a;
				} else if (bo->format == DRM_FORMAT_RGBA8888) {

					//log_i("bo->format grba888\n");

					pixel[0] = r;
					pixel[1] = g;
					pixel[2] = b;
					pixel[3] = a;
				}
			}
			else
			{
				pixel = row + j * 4;

				r = 0x00;
				g = 0x00;
				b = 0x00; 

				pixel[0] = r;
				pixel[1] = g;
				pixel[2] = b;
				pixel[3] = a;

			}
		}
	}

   return 0;
}


int rkdrm_show_buff_hdmi(uint32_t x, uint32_t y, uint32_t width,uint32_t height, const char *buff)
{
   struct sp_bo* bo = NULL;

   uint8_t a = 0;
   uint8_t r = 0;
   uint8_t g = 0;
   uint8_t b = 0;

   uint8_t* pixel = NULL;

   uint32_t i, j, xmax = x + width, ymax = y + height;


   if(!mDev->crtcs[0].scanout)
	   return -1;

   bo = mDev->crtcs[0].scanout;

#if 0
   log_i("width  =%d\n",bo->width);
   log_i("height =%d\n",bo->height);
   log_i("pitch  =%d\n",bo->pitch);
   log_i("size   =%d\n",bo->size);
#endif

#if 1
   if (xmax > bo->width)
	   xmax = bo->width;
   if (ymax > bo->height)
	   ymax = bo->height;
#endif

#if 0
   memset(bo->map_addr,0,bo->size);

   for (i = y; i < ymax; i++) {
	   uint8_t* row = (uint8_t*)bo->map_addr + i * bo->pitch;

	   for (j = x; j < xmax; j++) {
		   uint8_t* pixel = row + j * 4;

		   r = (*(buff + 0))&0x000000ff;
		   g = (*(buff + 1))&0x000000ff;
		   b = (*(buff + 2))&0x000000ff; 
		   		   
		   buff = buff + 3;

		   if (bo->format == DRM_FORMAT_ARGB8888 || bo->format == DRM_FORMAT_XRGB8888) {
			   pixel[0] = b;
			   pixel[1] = g;
			   pixel[2] = r;
			   pixel[3] = a;
		   } else if (bo->format == DRM_FORMAT_RGBA8888) {
			   pixel[0] = r;
			   pixel[1] = g;
			   pixel[2] = b;
			   pixel[3] = a;
		   }
	   }
   }
#elif 1

	for(i=0;i<HDMI_LCD_HEIGHT;i++)
	{
		uint8_t* row = (uint8_t*)bo->map_addr + i * bo->pitch;

		for(j=0;j<HDMI_LCD_WIDTH;j++)
		{
			if((i >= y)&&(i < (y + height))&&(j >= x)&&(j < (x + width)))
			{
				pixel = row + j * 4;
				
				r = (*(buff + 0))&0x000000ff;
				g = (*(buff + 1))&0x000000ff;
				b = (*(buff + 2))&0x000000ff; 
						
				buff = buff + 4;
				
				if (bo->format == DRM_FORMAT_ARGB8888 || bo->format == DRM_FORMAT_XRGB8888) {
					pixel[0] = b;
					pixel[1] = g;
					pixel[2] = r;
					pixel[3] = a;
				} else if (bo->format == DRM_FORMAT_RGBA8888) {
					pixel[0] = r;
					pixel[1] = g;
					pixel[2] = b;
					pixel[3] = a;
				}
			}
			else
			{
				pixel = row + j * 4;

				r = 0x00;
				g = 0x00;
				b = 0x00; 

				pixel[0] = r;
				pixel[1] = g;
				pixel[2] = b;
				pixel[3] = a;

			}
		}
	}
#elif 1

	for(i=0;i<1080;i++)
	{
		uint8_t* row = (uint8_t*)bo->map_addr + i * bo->pitch;

		for(j=0;j<1920;j++)
		{
			pixel = row + j*4;
			
			r = 0x00;
			g = 0xff;
			b = 0x00; 
			
			pixel[0] = r;
			pixel[1] = g;
			pixel[2] = b;
			pixel[3] = a;
		}
	}
#elif 0

	memset(bo->map_addr,0xa5,bo->size);

#endif
   return 0;
}


int rkdrm_show_buff_hdmi2(const char *buff,const int size)
{
	int swap_w = 0;
	int swap_h = 0;
    int width, height;
	int x,y,w,h;
	struct sp_bo *bo;
    uint32_t handles[4], pitches[4], offsets[4];
    int ret, fd, err;

	char * src_data = NULL;
	char * dst_data = NULL;
	
	MppBuffer dst_buffer = NULL;

	log_i("size=%d\n",size);

	mpp_buffer_get(NULL, &dst_buffer, size); //申请内存

	dst_data = mpp_buffer_get_ptr(dst_buffer);

	memcpy(dst_data,buff,size);

	swap_w = 3840;//;
	swap_h = 2160;//;

	fd = mpp_buffer_get_fd(dst_buffer);


    bo = (struct sp_bo *)calloc(1, sizeof(struct sp_bo));
    if (!bo) {
        rkdrm_log("failed to calloc bo.\n");
        return -2;
    }

    //drmPrimeFDToHandle(mDev->fd, fd, &bo->handle);
    bo->dev    = mDev;
    bo->width  = swap_w;
    bo->height = swap_h;
    bo->depth  = 16;
    bo->bpp    = 24;
    bo->format = DRM_FORMAT_RGB888;
    bo->flags  = 1;

    handles[0] = bo->handle;
    pitches[0] = swap_w;
    offsets[0] = 0;
    handles[1] = bo->handle;
    pitches[1] = swap_w;
    offsets[1] = swap_w * swap_h;
	
    ret = drmModeAddFB2(mDev->fd, bo->width, bo->height,
                        bo->format, handles, pitches, offsets,
                        &bo->fb_id, bo->flags);
    if (ret != 0) {
        rkdrm_log("failed to exec drmModeAddFb2,ret=%d.\n",ret);
        return -3;
    }


	if(swap_w > swap_h)
	{
		w = LCD_W;
		h = swap_h*LCD_W/swap_w;
		if(h > LCD_H)
		{
			h = LCD_H;
			w = swap_w*h/swap_h;
		}
		
		x = (LCD_W - w)/2;
		y = (LCD_H - h)/2;
	}
	else
	{
		h = LCD_H;
		w = swap_w*LCD_H/swap_h;
		if(w > LCD_W)
		{
			w = LCD_W;
			h = swap_h*w/swap_w;
		}
				
		x = (LCD_W - w)/2;
		y = (LCD_H - h)/2;
	}


    ret = drmModeSetPlane(mDev->fd, mTestPlane_rgb888->plane->plane_id,
                          mCrtc->crtc->crtc_id, bo->fb_id, 0,
                          x, y,   // display x, y
                          w, 	  //1920,//mCrtc->crtc->mode.hdisplay, // display width
                          h,      // 1080,//mCrtc->crtc->mode.vdisplay, // display height
                          0, 0, bo->width << 16, bo->height << 16);
    if (ret) {
        rkdrm_log("failed to exec drmModeSetPlane.\n");
        return -3;
    }

    if (mTestPlane_rgb888->bo) {
        if (mTestPlane_rgb888->bo->fb_id) {
            ret = drmModeRmFB(mDev->fd, mTestPlane_rgb888->bo->fb_id);
            if (ret)
                rkdrm_log("failed to exec drmModeRmFB.\n");
        }
        if (mTestPlane_rgb888->bo->handle) {
            struct drm_gem_close req = {
                    .handle = mTestPlane_rgb888->bo->handle,
            };

            drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
        }
        free(mTestPlane_rgb888->bo);
    }
    mTestPlane_rgb888->bo = bo;

	log_d("\n");

	if(dst_buffer){
		mpp_buffer_put(dst_buffer);
		dst_buffer = NULL;
	}
	log_d("\n");


    return 0;
}



void * rkdrm_get_map_addr(void)
{
	struct sp_bo* bo = NULL;

	if(!mDev->crtcs[1].scanout)
	   return NULL;

	bo = mDev->crtcs[1].scanout;

	return bo->map_addr;
}

uint32_t rkdrm_get_buff_width(void)
{
	struct sp_bo* bo = NULL;

	if(!mDev->crtcs[1].scanout)
	   return NULL;

	bo = mDev->crtcs[1].scanout;

	return bo->width;
}			   


int rga_nv12data_rotation(char *srcdata, char *dstdata,int in_w ,int in_h)
{
	int ret = 0, while_time = 0;

	IM_USAGE		ROTATE;

	IM_STATUS		STATUS;

	im_rect 		src_rect;
	im_rect 		dst_rect;
	rga_buffer_t	src;
	rga_buffer_t	dst;

	char* src_buf	 = srcdata;
	char* dst_buf	 = dstdata;

	int   src_width  = in_w;
	int   src_height = in_h;

	int   dst_width  = in_h;
	int   dst_height = in_w;

	memset(&src_rect, 0, sizeof(src_rect));
	memset(&dst_rect, 0, sizeof(dst_rect));
	memset(&src, 0, sizeof(src));
	memset(&dst, 0, sizeof(dst));

	//MPP_FMT_YUV420SP--RK_FORMAT_YCbCr_420_SP

	//RK_FORMAT_YCbCr_420_SP = 0xa << 8,

	src = wrapbuffer_virtualaddr(src_buf, src_width, src_height, RK_FORMAT_YCbCr_420_SP);

	//
	dst = wrapbuffer_virtualaddr(dst_buf, dst_width, dst_height, RK_FORMAT_YCbCr_420_SP);

	if(src.width == 0 || dst.width == 0)
	{
		log_e("error-%s\n",imStrError());
		return ERROR;
	}

	ret = imconfig(IM_CONFIG_SCHEDULER_CORE,RGA3_SCHEDULER_CORE1); 
	if(ret < 0)
	{
		log_e("imconfig error-%s\n",imStrError());
		return ERROR;
	}
	
	do {
		if (while_time) {
			static int while_num = 1;
			log_i("This is %d time in the loop\n", while_num);

			while_num++;
			while_time--;
		}
		/********** Execution function according to mode **********/

		dst.width	= src.height;
		dst.height	= src.width;
		dst.wstride = src.hstride;
		dst.hstride = src.wstride;

		ROTATE = IM_HAL_TRANSFORM_ROT_270;

		ret = imcheck(src, dst, src_rect, dst_rect, ROTATE);
		if (IM_STATUS_NOERROR != ret) {
			log_e("check error! %s\n", imStrError((IM_STATUS)ret));
			return -1;
		}

		ret = imrotate(src, dst, ROTATE);
		if(ret != IM_STATUS_SUCCESS){
			log_e("rotate error! %s\n", imStrError((IM_STATUS)ret));
			return -1;
		}

		if (while_time) {
			/* 200ms */
			usleep(200000);
		}
	}while(while_time);


	return 0;
}

int rkdrm_write_file(const char * root,int size, const char* filename)
{
	int ret = -1;
	int fd  = -1;

	
	if(!root)
		return -1;

	if(!filename)
		return -1;


	ret = remove(filename);

	fd = open(filename, O_CREAT|O_WRONLY);
	if(fd == (-1)){
		log_e("open file %s error\n",filename);
		return ;
	}

	write(fd,root,size);

	close(fd);
	fd = -1;

	return 0;
}


int rkdrm_display(MppFrame frame)
{
	int swap_w = 0;
	int swap_h = 0;
    int width, height;
	int size  = 0;
	MppFrameFormat frame_format;
	int x,y,w,h;
	struct sp_bo *bo;
    uint32_t handles[4], pitches[4], offsets[4];
    int ret, fd, err;

	char * src_data = NULL;
	char * dst_data = NULL;
	
	MppBuffer dst_buffer = NULL;

    err = mpp_frame_get_errinfo(frame)|mpp_frame_get_discard(frame);
    if (err) {
        rkdrm_log("get err info %d discard %d, go back.\n",
                mpp_frame_get_errinfo(frame),
                mpp_frame_get_discard(frame));
        return -1;
    }

    width  = mpp_frame_get_width(frame);
    height = mpp_frame_get_height(frame);
    width  = CODEC_ALIGN(width, 16);
    height = CODEC_ALIGN(height, 16);

	swap_w = width;
	swap_h = height;

	frame_format = mpp_frame_get_fmt(frame); //MPP_FMT_YUV420SP
	size = mpp_buffer_get_size(mpp_frame_get_buffer(frame));


	#if 1
	mpp_buffer_get(NULL, &dst_buffer, size); //申请内存

	src_data = mpp_buffer_get_ptr(mpp_frame_get_buffer(frame));
	dst_data = mpp_buffer_get_ptr(dst_buffer);

	rga_nv12data_rotation(src_data,dst_data,swap_w,swap_h);

	swap_w = height;
	swap_h = width;


	fd = mpp_buffer_get_fd(dst_buffer);
	#else

	swap_w = width;
	swap_h = height;
	
	fd = mpp_buffer_get_fd(mpp_frame_get_buffer(frame));

	#endif

    bo = (struct sp_bo *)calloc(1, sizeof(struct sp_bo));
    if (!bo) {
        rkdrm_log("failed to calloc bo.\n");
        return -2;
    }


    drmPrimeFDToHandle(mDev->fd, fd, &bo->handle);
    bo->dev    = mDev;
    bo->width  = swap_w;
    bo->height = swap_h;
    bo->depth  = 16;
    bo->bpp    = 32;
    bo->format = DRM_FORMAT_NV12;
    bo->flags  = 1;

    handles[0] = bo->handle;
    pitches[0] = swap_w;
    offsets[0] = 0;
    handles[1] = bo->handle;
    pitches[1] = swap_w;
    offsets[1] = swap_w * swap_h;
	
    ret = drmModeAddFB2(mDev->fd, bo->width, bo->height,
                        bo->format, handles, pitches, offsets,
                        &bo->fb_id, bo->flags);
    if (ret != 0) {
        rkdrm_log("failed to exec drmModeAddFb2.\n");
        return -3;
    }


	if(swap_w > swap_h)
	{
		w = LCD_W;
		h = swap_h*LCD_W/swap_w;
		if(h > LCD_H)
		{
			h = LCD_H;
			w = swap_w*h/swap_h;
		}
		
		x = (LCD_W - w)/2;
		y = (LCD_H - h)/2;
	}
	else
	{
		h = LCD_H;
		w = swap_w*LCD_H/swap_h;
		if(w > LCD_W)
		{
			w = LCD_W;
			h = swap_h*w/swap_w;
		}
				
		x = (LCD_W - w)/2;
		y = (LCD_H - h)/2;
	}


    ret = drmModeSetPlane(mDev->fd, mTestPlane->plane->plane_id,
                          mCrtc->crtc->crtc_id, bo->fb_id, 0,
                          x, y,   // display x, y
                          w, 	  //1920,//mCrtc->crtc->mode.hdisplay, // display width
                          h,      // 1080,//mCrtc->crtc->mode.vdisplay, // display height
                          0, 0, bo->width << 16, bo->height << 16);
    if (ret) {
        rkdrm_log("failed to exec drmModeSetPlane.\n");
        return -3;
    }

    if (mTestPlane->bo) {
        if (mTestPlane->bo->fb_id) {
            ret = drmModeRmFB(mDev->fd, mTestPlane->bo->fb_id);
            if (ret)
                rkdrm_log("failed to exec drmModeRmFB.\n");
        }
        if (mTestPlane->bo->handle) {
            struct drm_gem_close req = {
                    .handle = mTestPlane->bo->handle,
            };

            drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
        }
        free(mTestPlane->bo);
    }
    mTestPlane->bo = bo;

	log_d("\n");

	if(dst_buffer){
		mpp_buffer_put(dst_buffer);
		dst_buffer = NULL;
	}
	log_d("\n");


    return 0;
}

int rkdrm_fini(void)
{
    if (mDev) destroy_sp_dev(mDev);
    if (mPlanes) free(mPlanes);
}
